# -*- coding: utf-8 -*-
"""
Created on Wed Nov  6 22:32:41 2019

@author: XCL01
"""

import numpy as np
import matplotlib.pyplot as plt
import random
import pandas as pd
import math

#初始化聚类中心
def init_center(datamat,k):
    random.seed(111)#随机数种子，确保每次实验的随机数是一致的
    n = np.shape(datamat)[0]  # 获得样本的个数
    all_index = list(range(n))
    random.shuffle(all_index)  # 随机排序取得前k个
    center_index = all_index[0:k]
    center_point  = datamat[center_index]
    return center_point    #矩阵类型     

def dist_eclud(centers, one_sample):  # 计算两个点的距离
    d = centers - one_sample
    dd = np.sum(d ** 2, axis=0)
    return dd

def kmeans(datamat,k): # 聚类算法的主体部分
    n=np.shape(datamat)[0]#样本总数
    cluster_assment =np.mat(np.zeros((n,2))) # 新建一个存储[族序号，距离的平方]
    center_point =init_center(datamat,k)#初始化
    print('最初的聚类中心为:',center_point )
    clusterChanged=True#循环标志位
    iteration_time=0#记录循环次数
    while clusterChanged:
        clusterChanged = False
        for i in range(n):
            min_Dist = np.inf;# 初始定义距离为无穷大
            min_Index = -1# 初始化索引值
            for j in range(k):# 计算每个样本与k个中心点距离
                dist_J = dist_eclud(datamat[i],center_point [j] )  # 计算第i个样本到第j个中心点的距离
                if dist_J < min_Dist: # 判断距离是否为最小
                    min_Dist = dist_J # 更新获取到最小距离
                    min_Index = j# 获取对应的簇序号
            if cluster_assment [i, 0] != min_Index: # 样本上次分配结果跟本次不一样，标志位clusterChanged置True
                clusterChanged = True
            cluster_assment [i, :] = min_Index, min_Dist ** 2  # 分配样本到最近的簇            
        iteration_time += 1
        sse = sum(cluster_assment [:, 1])
        print('第{}次迭代结果，畸变函数值为{}'.format(iteration_time, sse))
        for cent in range(k):  # 样本分配结束后，重新计算聚类中心
            ptsInClust = datamat[np.nonzero(cluster_assment [:, 0].A == cent)[0]]# 获取该簇所有的样本点
            center_point [cent] = np.mean(ptsInClust, axis=0)# 更新聚类中心：axis=0沿列方向求均值。
    print('最终的聚类中心为：{}'.format(center_point))
    return center_point , cluster_assment 

#绘制original result
def originalDatashow(datamat):    
    num,dim = np.shape(datamat)    
    for i in range(num):
        mat_index=int(datamat.iat[i,2])
        plt.plot(datamat.iat[i,0],datamat.iat[i,1],marksamples[mat_index],markersize=5)
    plt.title('original result')
    plt.xlabel('x lable')
    plt.ylabel('y lable')
    plt.show()

#极坐标变换 
def rectangular_polar(datamat):
    n, dim = np.shape(datamat)
    data_ang = np.zeros((n, dim))
    for i in range(n):
        data_ang[i,0]=np.sqrt(np.power(datamat.iat[i, 0], 2) + np.power(datamat.iat[i, 1], 2))
        data_ang[i, 1] = math.atan(datamat.iat[i, 1] / datamat.iat[i, 0])
        if datamat.iat[i, 0] < 0:
            data_ang[i, 1] += np.pi
        if datamat.iat[i, 0] == 0:
            data_ang[i, 1] = np.pi / 2
    return data_ang
    
#画图   
def kmeans_show(datamat,datamat_angel):
    num, dim = np.shape(datamat)  # 样本数num ,维数dim
    center_point , cluster_assment  = kmeans(datamat_angel,k)
    for i in range(num): #所有样本
        markindex = int(cluster_assment [i, 0])  # 矩阵形式转为int值, 簇序号
        plt.plot(datamat.iat[i, 0], datamat.iat[i, 1], marksamples[markindex], markersize=6)# 特征维对应极坐标轴r,theter；样本图形标记及大小   
    for i in range(k): #圆周 
        x = center_point [i] * np.cos(theta)
        y = center_point [i] * np.sin(theta)
        plt.plot(x, y, center_point [i], markersize=3, label=label[i], c=c[i])
    plt.title('k_mean_result')
    plt.xlabel('x lable')
    plt.ylabel('y lable')
    plt.show()

if __name__ == '__main__':
    k=5 #聚类数量
    marksamples = ['or', 'ob', 'og', 'ok','^b', '<g' ,'^r', 'ob']#样本初始化
    markcentroids = ['*', '^','<','p','o', '1','2','3']  # 聚类中心图形标记
    label = ['1', '2', '3', '4', '5', '6', '7', '8'] #聚类中心标签
    c = ['yellow', 'black', 'red', 'blue', 'green','orange','black', 'red']#聚类中心颜色
    theta =np.arange(0,np.pi*2,0.01)
    datamat = pd.read_csv("D:\Files\machinelearning_homework\homework_03_kmeans\dataset_circles.csv",header=None)#加载数据
    datamat = datamat.loc[:,[0,1,2]]     
    datalist = rectangular_polar(datamat)
    kmeans_show(datamat,datalist[:,0]) 
    originalDatashow(datamat)